package com.kongt.cluster.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RedisClusterService {
    private static String NODE_KEY_PREFIX = "node:";
    private static String MASTER_KEY = "master";
    private static final String ALIVE_STATUS = "alive";
    // 节点状态过期时间（秒）
    private static final int NODE_STATUS_EXPIRATION = 20;
    private final String nodeId;
    private final String appName;
    private final StringRedisTemplate stringRedisTemplate;
    private ScheduledExecutorService scheduler;

    //当当前节点当选为主节点
    private ElectMasterEvent electMasterEvent;

    //都掉主节点触发事件
    private LossMasterEvent lossMasterEvent;

    /**
     * @return 是否为master节点
     */


    /**
     * 构造RedisClusterService实例
     *
     * @param appName             应用名称，用于标识服务所属的应用
     * @param nodeId              节点ID，用于标识集群中的特定节点
     * @param stringRedisTemplate Redis模板，用于操作Redis集群中的字符串数据
     * @param electMasterEvent    选主事件处理器，当选举新主节点时触发
     * @param lossMasterEvent     失主事件处理器，当失去主节点身份时触发
     */
    public RedisClusterService(String appName, String nodeId, StringRedisTemplate stringRedisTemplate, ElectMasterEvent electMasterEvent, LossMasterEvent lossMasterEvent) {
        NODE_KEY_PREFIX = appName + ":" + NODE_KEY_PREFIX;
        MASTER_KEY = appName + ":" + MASTER_KEY;


        this.nodeId = nodeId;
        this.appName = appName;
        this.stringRedisTemplate = stringRedisTemplate;
        this.electMasterEvent = electMasterEvent;
        this.lossMasterEvent = lossMasterEvent;
        this.scheduler = Executors.newScheduledThreadPool(1);
    }

    // 启动节点，定时汇报生存状态
    public void start() {
        //当有新节点启动重新竞选
        deleteMasterKey();

        reportNodeAlive();

        // 尝试选举Master
        electMaster();

        // 启动检查节点状态的定时任务
        checkNodeStatuses();
    }

    private void reportNodeAlive() {
        scheduler.scheduleAtFixedRate(() -> {
            stringRedisTemplate.opsForValue().set(NODE_KEY_PREFIX + nodeId, ALIVE_STATUS, NODE_STATUS_EXPIRATION, TimeUnit.SECONDS);
            log.info("Node " + appName + ":" + nodeId + " reported alive.");
        }, 0, 10, TimeUnit.SECONDS);
    }

    private void deleteMasterKey() {
        log.info("新节点启动,清理master信息,重新竞选!!!");
        stringRedisTemplate.delete(MASTER_KEY);
    }

    // 选举Master
    private void electMaster() {
        synchronized (this) {
            String currentMaster = stringRedisTemplate.opsForValue().get(MASTER_KEY);
            if (currentMaster == null) {
                // 没有Master，尝试成为Master
                Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(MASTER_KEY, nodeId);
                if (success) {
                    this.electMasterEvent.onElectedMaster(new DefaultEventParmas(nodeId));
                    log.info("Node " + appName + ":" + nodeId + " elected as master.");
                } else {
                    log.info("Another node is already master.");
                }
            } else {
                // 已有Master，检查其是否存活
                if (!isNodeAlive(currentMaster)) {
                    // Master已死亡，重新选举
                    log.info("Master " + currentMaster + " is dead. Re-electing master...");
                    deleteMasterKey();
                    electMaster();
                } else {
                    log.info("Current master " + currentMaster + " is still alive.");
                }
            }
        }
    }

    // 检查节点状态的定时任务
    private void checkNodeStatuses() {
        scheduler.scheduleAtFixedRate(() -> {
            Set<String> allNodes = stringRedisTemplate.keys(NODE_KEY_PREFIX + "*");
            String currentMaster = stringRedisTemplate.opsForValue().get(MASTER_KEY);
            for (String nodeKey : allNodes) {
                String node = nodeKey.replace(NODE_KEY_PREFIX, "");
                if (!isNodeAlive(node)) {
                    //如果有节点挂了
                    log.info("Node " + node + " is dead.");
                    if (node.equals(currentMaster)) {
                        // 如果死亡的节点是主节点，重新选举
                        log.info("Dead node is the master. Re-electing master...");
                        deleteMasterKey();
                        electMaster();
                    }
                }
                if (node.equals(this.nodeId) && (!node.equals(currentMaster))) {
                    // 如果死亡的节点是主节点，重新选举
                    log.info("loss the master.report...");
                    lossMasterEvent.onLossMaster(
                            null
                    );
                }


            }
        }, 0, 30, TimeUnit.SECONDS);
    }

    // 检查节点是否存活
    private boolean isNodeAlive(String nodeId) {
        String status = stringRedisTemplate.opsForValue().get(NODE_KEY_PREFIX + nodeId);
        return ALIVE_STATUS.equals(status);
    }

    // 关闭节点
    public void stop() {
        scheduler.shutdown();
    }

}